using UnityEngine;
using System.Collections;
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;



public class UnityVRPN {

	//NATIVE vrpn functions
	

#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void getButtonData(string name, int num_buttons, int[] data, out long timestamp);
	
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void getTrackerData(string name, int sensor,  double[] pos, double[] quat, out long timestamp);
				
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void getAnalogData(string name, int num_channels, double[] data, out long timestamp);
	
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void getTextData(string name, StringBuilder text, out long timestamp);
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void setAnalogOutput(string name, int num_channels, double[] data);
		
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void sendTextData(string name, int port, string text);
		
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void sendAnalogData(string name, int port, int num, double[] data);
		
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void sendButtonData(string name, int port, int num, int[] data);
		
#if UNITY_IPHONE
	[DllImport ("__Internal")]
#else
	[DllImport ("UnityVRPN")]
#endif
private static extern void sendTrackerData(string name, int port, int sensor, double[] pos, double[] quat);
	
	public static bool useWrapper = false;
	
	//class function
	public static void getButtonInfo(string name, int num_buttons, int[] data){
		if(useWrapper){
			UnityVRPNSocket.getButtonInfo(name,num_buttons,data);
		}else{
			long timestamp = 0;
			getButtonData(name,num_buttons,data,out timestamp);
		}
	}
	public static void getTrackerInfo(string name, int sensor, float[] pos, float[] quat){
		double[] posD = new double[pos.Length];
		double[] quatD = new double[quat.Length];
		if(useWrapper){
			UnityVRPNSocket.getTrackerInfo(name,sensor,posD,quatD);
		}else{
			long timestamp = 0;
			getTrackerData(name,sensor,posD,quatD,out timestamp);
		}
		for(int i=0;i<3;i++){
			pos[i] = (float)posD[i];
			quat[i] = (float)quatD[i];
		}
		quat[3] = (float)quatD[3];
	}
	public static void getAnalogInfo(string name, int num_channels, float[] data){
		double[] dataD = new double[data.Length];
		if(useWrapper){
			UnityVRPNSocket.getAnalogInfo(name, num_channels,dataD);
		}else{
			long timestamp = 0;
			getAnalogData(name, num_channels,dataD, out timestamp);
		}
		for(int i=0;i<data.Length;i++){
			data[i] = (float)dataD[i];
		}
	}

	
	public static  void getTextInfo(string name, StringBuilder text){
		if(useWrapper){
			string text2="";
			UnityVRPNSocket.getTextInfo(name,out text2);
			text.Append(text2);
		}else{
			long timestamp = 0;
			getTextData(name,text,out timestamp);
		}
	}
	
	public static  void sendAnalogOutput(string name, int num, float[] outputs){
		double[] temp = new double[num];
		for(int i=0;i<num;i++){
			temp[i] = (double)outputs[i];
		}
		if(useWrapper){
			UnityVRPNSocket.sendAnalogOutput(name, num, temp);
		}else{
			Debug.Log("here");
			setAnalogOutput(name,num,temp);
		}
	}
	
	public static  void sendTextInfo(string name, int port, string text){
		if(useWrapper){
			UnityVRPNSocket.sendTextInfo(name,port,text);
		}else{
			sendTextData(name,port,text);
		}
	}

	public static  void sendAnalogInfo(string name, int port, int num, float[] data){
		double[] dataD = new double[data.Length];
		for(int i=0;i<data.Length;i++){
			dataD[i] = data[i];
		}
		if(useWrapper){
			UnityVRPNSocket.sendAnalogInfo(name,port,num,dataD);
		}else{
			sendAnalogData(name,port,num,dataD);
		}
	}

	public static  void sendButtonInfo(string name, int port, int num, int[] data){
		if(useWrapper){
			UnityVRPNSocket.sendButtonInfo(name,port,num,data);
		}else{
			sendButtonData(name,port,num,data);
		}
	}

	public static  void sendTrackerInfo(string name, int port, int sensor, float[] pos, float[] quat){
		double[] posD = new double[pos.Length];
		double[] quatD = new double[quat.Length];
		for(int i=0;i<3;i++){
			posD[i] = pos[i];
			quatD[i] = quat[i];
		}
		quatD[3] = quat[3];
		if(useWrapper){
			UnityVRPNSocket.sendTrackerInfo(name,port,sensor,posD,quatD);
		}else{
			sendTrackerData(name,port,sensor,posD,quatD);
		}
	}
	
}
class UnityVRPNSocket {
	
	//You'll need one of these per interface server (per machine)
	public static string message = ""; //the message received from the server (so we can see it)
	public static string server_address="localhost"; //the address of the server
	public static int server_port=46761; //the port of the server
	
	//C# networking stuff
	private static TcpClient tcpclnt = null; 
	private static NetworkStream stm = null; 
	private static ASCIIEncoding asen = null;
	
	// Static constructor
    static UnityVRPNSocket()
    {
        try {
            message = "";
            tcpclnt = new TcpClient();
            tcpclnt.Connect(server_address,server_port);
            // use the ipaddress as in the server program
             stm = tcpclnt.GetStream();
			asen= new ASCIIEncoding();
            
        }
        
        catch (Exception e) {
			Debug.Log(e.Message);
        }
    }
	//name is the full name of the butter server with port, i.e. Button0@localhost:3883
	//num_buttons is the number of buttons you are retrieving.  Do not exceed that which is available
	//data are the outputs, the state of each button 0 for off, 1 for on
	public static void getButtonInfo(string name, int num_buttons, int[] data){
		try{
            byte[] ba=asen.GetBytes("button"+"\t"+name+"\t"+num_buttons);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2000];
            int k=stm.Read(bb,0,2000);
			if(k > 0){
            	message = asen.GetString(bb);
				string[] split = message.Split(new char[]{'\t'});
				
				for(int i=0;i<num_buttons;i++){
					data[i] = int.Parse(split[i]);
				}
				
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//name is as above, i.e. Tracker0@localhost:3883
	//sensor is which number sensor (zero indexed) you are retrieving the data for
	//pos and quat outputs are [x,y,z],[x,y,z,w] respectively
	public static void getTrackerInfo(string name, int sensor, double[] pos, double[] quat){
		
		try{
            byte[] ba=asen.GetBytes("tracker"+"\t"+name+"\t"+sensor);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[1000];
            int k=stm.Read(bb,0,1000);
			if(k > 0){
            	message = asen.GetString(bb);
				string[] split = message.Split(new char[]{'\t'});
				for(int i=0;i<3;i++){
					pos[i] = double.Parse(split[i]);
				}
				for(int i=3;i<7;i++){
					quat[i-3] = double.Parse(split[i]);
				}
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
		
	}
	//name is as above, i.e. Analog0@localhost:3883
	//num_channels is the total number of channels you are retrieving, e.g. 7
	//data is the analog data for each channel  retrieved from the server
	public static void getAnalogInfo(string name, int num_channels, double[] data){
		try{
            byte[] ba=asen.GetBytes("analog"+"\t"+name+"\t"+num_channels);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2000];
            int k=stm.Read(bb,0,2000);
			if(k > 0){
            	message = asen.GetString(bb);
				
				string[] split = message.Split(new char[]{'\t'});
				
				for(int i=0;i<num_channels;i++){
					data[i] = double.Parse(split[i]);
				}
				
			}   
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	public static void sendAnalogOutput(string name, int num_channels, double[] data){
		try{
			string temp = "analog_output"+"\t"+name+"\t"+num_channels;
			for(int i =0;i<num_channels;i++){
				temp = temp+"\t"+data[i];
			}
            byte[] ba=asen.GetBytes(temp);
			
			stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				//Debug.Log(message);
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//name is as above, e.g. Text0@localhost:3883
	//text is what is retrieved from the server
	public static void getTextInfo(string name, out string text){
		text = "";
		try{
            byte[] ba=asen.GetBytes("text"+"\t"+name);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				string[] split = message.Split(new char[]{'\t'});
				//Debug.Log(message);
				text = split[0];
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//Send some text
	//name is the name of the text server, e.g. Text1
	//port is the port of your server, e.g. 3884
	//text is the text to send
	public static void sendTextInfo(string name, int port, string text){
		try{
            byte[] ba=asen.GetBytes("text_sender"+"\t"+name+"\t"+port+"\t"+text);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				//Debug.Log(message);
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//send some tracker data
	//name is the name of the tracker server, e.g. Tracker1
	//port is the port of your server, e.g. 3884
	//sensor is which of your sensors you are sending
	//pos is the [x,y,z] position to send (should be in VRPN standard meters)
	//quat is the [x,y,z,w] quaternion to send
	public static void sendTrackerInfo(string name, int port, int sensor, double[] pos, double[] quat){
		try{
            byte[] ba=asen.GetBytes("tracker_sender"+"\t"+name+"\t"+port+"\t"+sensor +"\t" + pos[0] + "\t" + pos[1]+ "\t" + pos[2] + "\t" + quat[0] + "\t" + quat[1] + "\t" + quat[2] + "\t" + quat[3]);
            stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				//Debug.Log(message);
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//send some analog values
	//name is the name of the analog server, e.g. Analog1
	//port is the port of your server, e.g. 3884
	//num_analogs is the number of analog channels you have total
	//data is the array of channel data to send
	public static void sendAnalogInfo(string name, int port, int num_analogs, double[] data){
		try{
			string temp = "analog_sender"+"\t"+name+"\t"+port+"\t"+num_analogs;
			for(int i =0;i<num_analogs;i++){
				temp = temp+"\t"+data[i];
			}
            byte[] ba=asen.GetBytes(temp);
			
			stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				//Debug.Log(message);
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}
	//send some button data
	//name is the name of the button server, e.g. Button1
	//port is the port of your server, e.g. 3884
	//num buttons is the total number of buttons you have
	//states are the 0 (off) or 1 (on) states of each button
	public static void sendButtonInfo(string name, int port, int num_buttons, int[] states){
		try{
			string temp = "button_sender"+"\t"+name+"\t"+port+"\t"+num_buttons;
			for(int i =0;i<num_buttons;i++){
				temp = temp+"\t"+states[i];
			}
            byte[] ba=asen.GetBytes(temp);
			
			stm.Write(ba,0,ba.Length);
            byte[] bb=new byte[2048];
            int k=stm.Read(bb,0,2048);
			if(k > 0){
            	message = asen.GetString(bb);
				//Debug.Log(message);
			}
            
		}
		catch(Exception e){
			Debug.Log(name + " connection broken: "+e.Message);
		}
	}	
}



